#include <memory>
#include <string>
#include <vector>
#include <cassert>
#include <sstream>
#include <iostream>
#include "message.hpp"
#include "format.hpp"
namespace bit
{
    // 格式化基类：
    class FormatItem
    {
    public:
        using ptr = std::shared_ptr<FormatItem>;
        virtual ~FormatItem(){}
        virtual void format(std::ostream &out, logMessage &msg) = 0;
    };
    // 格式化子项类:
    class MsgFormatItem : public FormatItem
    {
    public:
        void format(std::ostream &out, logMessage &msg) override
        {
            out << msg._message;
        }
    };
    class LevelFormatItem : public FormatItem
    {
    public:
        void format(std::ostream &out, logMessage &msg) override
        {
            out << level::level_tostring(msg._level);
        }
    };
    class TimeFormatItem : public FormatItem
    {
    public:
        void format(std::ostream &out, logMessage &msg) override
        {
            out << msg._time;
        }
    };
    class FileFormatItem : public FormatItem
    {
    public:
        void format(std::ostream &out, logMessage &msg) override
        {
            out << msg._filename;
        }
    };
    class LineFormatItem : public FormatItem
    {
    public:
        void format(std::ostream &out, logMessage &msg) override
        {
            out << msg._line;
        }
    };
    class LogFormatItem : public FormatItem
    {
    public:
        void format(std::ostream &out, logMessage &msg) override
        {
            out << msg._logname;
        }
    };
    class TidFormatItem : public FormatItem
    {
    public:
        void format(std::ostream &out, logMessage &msg) override
        {
            out << msg._tid;
        }
    };
    class OtherFormatItem : public FormatItem
    {
    public:
        // 构造函数：
        OtherFormatItem(const std::string &std) : _std(std) {}
        void format(std::ostream &out, logMessage &msg) override
        {
            out << _std;
        }

    private:
        std::string _std;
    };

    class Format :: FormatImpl
    {
    public:
        // 1.构造函数：
        FormatImpl(const std::string &pattern) : _pattern(pattern)
        {
            // 用户传过来的必须是一个有效的格式化字符，才能继续后面的工作：
            assert(_parsePattern());
        }

        // 下面这样设计的好处在于用户可以选择获取格式化后的字符串，也可以直接输出到任何输出流
        // （比如文件流、标准输出等）中，提供了灵活性
        // 2.1 对msg消息进行格式化，返回格式化之后的字符串：
        std::string format(logMessage msg)
        {
            std::stringstream ss;
            format(ss, msg);
            return ss.str();
        }
        // 2.2 重载format：把格式化之后的msg消息直接放到io流里面：
        void format(std::ostream &out, logMessage msg)
        {
            for (auto item : _items)
            {
                item->format(out, msg);
            }
        }

        //获取格式化的字符串：
        const std::string& pattern()
        {
            return _pattern;
        }
    private:
        // 3.解析格式化字符pattern，在解析的过程中完成对_items的初始化：
        bool _parsePattern()
        {
            // abc%%de%T[%p]
            bool ret = true;
            size_t pos = 0;
            while (pos <= _pattern.size())
            {
                char key = '\0';
                // 3.1 没有碰到%：
                std::string val;
                if (_pattern[pos] != '%')
                {
                    size_t tmp = pos++;
                    while (pos <= _pattern.size() && _pattern[pos] != '%')
                        pos++;
                    val += _pattern.substr(tmp, pos - tmp);
                }
                // 3.2 用户输入的是%%，旨在打印'%';
                else if (pos + 1 < _pattern.size())
                {
                    if (_pattern[pos + 1] == '%')
                    {
                        val += '%';
                        pos += 2;
                    }
                    // 3.3 用户输入不是原始字符：
                    else
                    {
                        key = _pattern[pos + 1];
                        pos += 2;
                    }
                }
                else
                {
                    ret = false;
                    std::cout << "格式化字符串解析错误..." << std::endl;
                    break;
                }

                if (ret != false)
                {
                    FormatItem::ptr tmp = _creaateItem(key, val);
                    if (tmp != nullptr)
                    {
                        _items.emplace_back(tmp);
                        val.clear();
                    }
                }
            }
            return ret;
        }
        // 根据不同的格式化字符，创建不同的格式化子项：
        FormatItem::ptr _creaateItem(const char key, const std::string &val)
        {

            FormatItem::ptr ret = nullptr;
            switch (key)
            {
            case '\0':
                ret = std::make_shared<OtherFormatItem>(val);
                break;
            case 'm':
                ret = std::make_shared<MsgFormatItem>();
                break;
            case 'p':
                ret = std::make_shared<LevelFormatItem>();
                break;
            case 'T':
                ret = std::make_shared<TimeFormatItem>();
                break;
            case 'f':
                ret = std::make_shared<FileFormatItem>();
                break;
            case 'l':
                ret = std::make_shared<LineFormatItem>();
                break;
            case 't':
                ret = std::make_shared<TidFormatItem>();
                break;
            case 'c':
                ret = std::make_shared<LogFormatItem>();
                break;
            default:
                std::cout << "%" << key << "不是一个有效的格式化字符！" << std::endl;
            }
            return ret;
        }

    private:
        std::string _pattern;
        std::vector<FormatItem::ptr> _items;
    };
    Format::Format(const std::string &pattern):_pImpl(std::make_shared<FormatImpl>(pattern)){}
    
    // 必须在析构函数之前让编译器看到FormatImpl的定义
    // 不然在析构计算FormatImpl的时候，是不知道FormatImpl大小的，就会报错sizeof
    // 所以你需要显示去定义，这就是解决这个问题的办法
    // 而编译器默认生成的，是在声明处就会生成代码，这个时候在format.hpp
    // 中，只看到class FormatImpl声明，没有定义，这是一个不完整的类型
    Format::~Format() = default;

    std::string Format::format(logMessage msg)
    {
        return _pImpl->format(msg);
    }
    void Format::format(std::ostream &out, logMessage msg)
    {
        return _pImpl->format(out, msg);
    }
    const std::string& Format::pattern()
    {
        return _pImpl->pattern();
    }
}